package org.jvnc.rfb;

/**
* <br><br><center><table border="1" width="80%"><hr>
* <p>
* Copyright (C) 2006-2007 by Deon Wu
* <p>
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.0
* of the License, or (at your option) any later version.
* <p>
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">GNU Lesser General Public License</a>
* for more details.
* <p>
* You should have received a copy of the <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">
* GNU Lesser General Public License</a> along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* <hr></table></center>
* **/

import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.Vector;

import org.jvnc.logging.VLog;
import org.jvnc.rfb.event.*;
import org.jvnc.rfb.proto.DataGram;
import org.jvnc.rfb.proto.server.ServerStatus;
import org.jvnc.rfb.state.*;
import org.jvnc.rfb.listener.*;

import org.jvnc.vnc.VncClient;



/**
 * RFB协议的客户端。 主要的功能有
 * 1. 封装RFB协议，并发给服务端。
 * 2. 读取从服务端读取协义内容封装后，交给VncClient处理。
 * 
 * 目前只支持RFB 003.008版协议。
 * 
 * 协议有如下几个阶段：
 * 1. 协议版本初始化。
 * 2. 认证信息初始化。
 * 3. 客户端与服务器环境初始化。
 * 4. 进入传输状态。
 * 
 * @author user
 *
 */

public class RFBSocketClient implements Runnable{
	private static VLog log = VLog.getLog();
	
	private Socket socket = null;
	private DataInput in = null;
	private DataOutputStream out = null;
	
	private String version = null;
	private float fVersion = 0;
	private String password = null;
	private byte initClient = 0;
	private boolean closed = false;
	
	private Collection<Listener> listeners = new Vector<Listener>();
	public ProtocolState handle;

	public RFBSocketClient(Socket socket, String version){
		//socket.
		this.socket = socket;
		this.version = version;
		
		this.initClient();
	}
	
	public RFBSocketClient(String host, int port, String version){
		try {
			this.socket = new Socket(host,port);
			this.version = version;
			//this(socket,version);
			this.initClient();
		} catch (UnknownHostException e) {
			log.error("Not found Host!",e);
		} catch (IOException e) {
			log.error(e.toString(),e);
		}
	}
	
	public RFBSocketClient(Socket socket){
		
	}
	
	private void initClient(){
		try {
			in = new DataInputStream(socket.getInputStream());
			out = new DataOutputStream(socket.getOutputStream());
			changeState(new InitProtocolVersion());
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		if(log.isDebug()){
			initDebug();
		}
	}
	
	
	private void initDebug(){
		
		/**
		 * 设置Socket读超时，为了防止在调试时出现程序疆死。
		 *
		 * 超时后Socket还可以继续使用。
		 */
		try {
			socket.setSoTimeout(1000 * 60);  
		} catch (SocketException e1) {
			log.error("Called setSoTimeOut()!", e1);
		}
		
		
		this.regListener(new ErrorListener(){
			public void handle(ErrorEvent e) {
				log.debug(e.getError());
			}
		});
		
		this.regListener(new ServerStatusListener(){
			public void handle(ServerStatusEvent e) {
				ServerStatus st = e.getServerStatus();
				log.debug("Screen: "+st.getName());
				log.debug("Frame width: "+st.getFrameWidth());
				log.debug("Frame Height: "+st.getFrameHeight());
				st.getPixelFormate().print(System.out);
			}
		});		
	}
	
	
	/**
	 * 改变协义当前的状态。
	 * @param handle
	 */
	public void changeState(ProtocolState handle){
		this.handle = handle;
		handle.setRFBSocket(this);
	}
	
	/**
	 * 线程运行方法。
	 */
	public void run() {
		
		RfbEvent event = null;
		try {
			while(!closed){
				synchronized(handle){
					event = handle.process();
					if(event != null){
						this.fireProtocolEvent(event);
					}
					out.flush();
				}
			}
			log.debug("RFBSocket Client is closed!");
		} catch (IOException e) {
			log.error(e.toString(),e);
		} finally{
			try{
				if(!closed){
					this.close();		
				}
				if(socket != null) socket.close();
			} catch(IOException e){
				log.error("Call close() error",e);
			}
			
		}

	}
	
	public void close() throws IOException{
		closed = true;
		if(socket != null) socket.close();
	}
	
	public DataInput getInputStream(){
		return in;
	}
	
	public DataOutput getOutputStream(){
		return out;
	}
	
	public String getProtocoVersion(){
		return version;
	}
	
	public float getVersion(){
		return this.fVersion;
	}
	
	public void initProtocoVersion(String version){
		this.version = version;
	}
	
	/**
	 * 设置VNC密码。
	 * @return
	 */
	public String getPassword(){
		return (this.password == null) ? "" : this.password; 
	}
	public void setPassowrd(String password){
		this.password = password;
	}
	
	/*
	 * 设置客户端初始化标志。true(独占） false(共享)
	 */
	public byte getInitClient(){
		return this.initClient;
	}
	public void getInitClient(byte initClient){
		this.initClient = initClient;
	}
	
	public void start(){
		Thread thread = new Thread(this);
		thread.start();
	}
	
/*	
	private void fireProtocolEvent(RfbEvent event) {
		try {
			log.debug("fireEvent:" + event.getClass().toString());
			for (Listener l : this.listeners) {
				log.debug("Matching the linstener:" + l.getClass().toString());
				for(Method m :l.getClass().getMethods()){
					
					Class[] param = m.getParameterTypes();
					if(param.length == 1 &&
						m.getName().equals("handle") &&
						param[0].equals(event.getClass())){
						
						log.debug("Matched the linstener:" + l.getClass().toString());
						log.debug("Event:" + event.toString());
						m.invoke(l, new Object[]{event});
						
					}
				}
			}
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}
*/	
	//使用反射机制委派事件处理，出现IllegalAccessException异常。
	private void fireProtocolEvent(RfbEvent event) {
		if (event instanceof ErrorEvent){
			for (Listener l : listeners) {
				if (l instanceof ErrorListener) {
					((ErrorListener)l).handle((ErrorEvent)event);
				}
			}
		}else if(event instanceof VersionEvent){
			for (Listener l : listeners) {
				if (l instanceof InitVersionListener) {
					((InitVersionListener)l).handle((VersionEvent)event);
				}
			}			
		}else if(event instanceof ServerStatusEvent){
			for (Listener l : listeners) {
				if (l instanceof ServerStatusListener) {
					((ServerStatusListener)l).handle((ServerStatusEvent)event);
				}
			}			
		}else if(event instanceof BellEvent){
			for (Listener l : listeners) {
				if (l instanceof RfbClientListener) {
					((RfbClientListener)l).handle((BellEvent)event);
				}
			}			
		}else if(event instanceof ColorMapEvent){
			for (Listener l : listeners) {
				if (l instanceof RfbClientListener) {
					((RfbClientListener)l).handle((ColorMapEvent)event);
				}
			}			
		}else if(event instanceof CutTextEvent){
			for (Listener l : listeners) {
				if (l instanceof RfbClientListener) {
					((RfbClientListener)l).handle((CutTextEvent)event);
				}
			}			
		}else if(event instanceof BufferUpdateEvent){
			for (Listener l : listeners) {
				if (l instanceof RfbClientListener) {
					((RfbClientListener)l).handle((BufferUpdateEvent)event);
				}
			}			
		}

	}


	public void regListener(Listener listener){
		listeners.add(listener);
	}
	
	/**
	 * 发布一个RFB协议报文。
	 * @param data 需要发送的报文。
	 * @return 如果发送成功返回true
	 * @throws IOException
	 */
	public boolean sendDataGram(DataGram data)	throws IOException{
		return sendDataGram(data, true);
	}
	
	/**
	 * 异步方式发送一个报文。
	 * @param data 需要发送的报文。
	 * @param isBlock 是否以阻塞方式发送。为false时，在新线程中发送报文。
	 * @return 发送成功返回true
	 * @throws IOException
	 */
	public boolean sendDataGram(DataGram data, boolean isBlock) throws IOException{
		if(this.handle != null){
			return handle.sendDataGram(data, isBlock);
		}else{
			return false;
		}
	}

}
